import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

// 属性和值不要有 < > 两个符号
// 属性值必须被引号包围，不过单引号和双引号均可使用
// 支持使用/*  */单行或多行注释，不支持 // 进行注释
// 尽量使用标准xml格式，否则会出现匹配异常
// 通过 bodyStr 获取 值，但需要注意，这值是指这个子节点内容，如果没有子类，则是获得的值，都是字符串
// 标签属性键不可重复，只会匹配第一个键，后面重复的会丢失
public class XMLUtil {
    //region 创建并初始化
    public XMLUtil(String xmlStr) {
        this.xmlStr = xmlStr;
        Reduce();
        Init();
    }

    private void Reduce() //xml 压缩
    {
        // 一下匹配需要注意先后顺序

        // /\*[\s\S]*?\*/
        // 移除多行注释
        // <[^<>]*?/>
        // 移除没用标签 移除 <book  /> 和 <book a="fdsa" /> 类型的标签
        // (?<=>)\s*(?=<)
        // 匹配标签中间的空白字符
        // ^\s*
        // 匹配开头空白字符

        String[] patternStrs = new String[]{"/\\*[\\s\\S]*?\\*/", "<[^<>]*?/>", "(?<=>)\\s*(?=<)", "^\\s*"};
        Pattern pattern = null;
        Matcher matcher = null;
        for (int i = 0; i < patternStrs.length; i++) {
            pattern = Pattern.compile(patternStrs[i]);
            matcher = pattern.matcher(xmlStr);
            if (matcher.find())//匹配成功
            {
                xmlStr = matcher.replaceAll("");
            }
        }
    }

    private void Init() {
        //region 初始化 头和身体 编码 版本
        // ^<\?xml[\w\W]+?\?>
        // 匹配 <?xml version="1.0" encoding="UTF-8"?>
        String patternStr = "^<\\?xml[\\w\\W]+?\\?>";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(xmlStr);
        if (matcher.find()) {
            headStr = matcher.group();
            bodyStr = xmlStr.substring(matcher.end());
            version = getAttrValue(headStr, "version");
            encoding = getAttrValue(headStr, "encoding");
        } else {
            bodyStr = xmlStr;
        }
        //endregion
        //region 解析xml 解析身体
        xmlObj = new XMLElement();
        xmlObj.parent = null;
        xmlObj.bodyStr = getBodyStr(bodyStr);
        xmlObj.headStr = getHeadStr(bodyStr);
        xmlObj.tagStr = getTagStr(xmlObj.headStr);
        xmlObj.attibutes = new HashMap<>();
        setAtttibutes(xmlObj.headStr, xmlObj.attibutes);
        // TODO 解析 子类集合
        parsingBody(xmlObj);
        //endregion
    }

    //endregion
    //region Util
    public void print(String msg) {
        Log.d("XMLUtil:", msg);
    }
    private String XMLElementToString(XMLElement xml)//XMLElement 对象转化成 String
    {
        String str="";
        if(xml!=null)
        {
            //region Head String
            str+="<"+xml.tagStr;
            if(xml.attibutes.size()!=0)
            {
                for(Map.Entry<String,String> entry : xml.attibutes.entrySet())
                {
                    if(entry.getValue().contains("\""))
                    {
                        str+=" "+entry.getKey()+"='"+entry.getValue()+"'";
                    }
                    else
                    {
                        str+=" "+entry.getKey()+"=\""+entry.getValue()+"\"";
                    }
                }
            }
            str+=">";
            //endregion
            //region Body String
            if(xml.childs.size()==0)
            {
                str+=xml.bodyStr;
            }
            else
            {
                for(int i=0;i<xml.childs.size();i++)
                {
                    str+=XMLElementToString(xml.childs.get(i));
                }
            }
            //endregion
            str+="</"+xml.tagStr+">";
        }
        return str;
    }
    //endregion
    //region 属性
    private String xmlStr;      // 要解析的xml字符串
    public String version = "";
    public String encoding = "";
    public XMLElement xmlObj = null;
    private String headStr = "";// <?xml version="1.0" encoding="UTF-8"?>
    private String bodyStr = "";// xml基本组成部分
    //endregion
    //region 内部解析(parsing)实现

    // matchHeadStr ： 要匹配的字符串,尖括号内容，根节点内容（headStr）
    // keyStr   ： 要匹配值的键
    private String getAttrValue(String matchHeadStr, String keyStr)//获取属性值
    {
        // (?<=version\s*=\s*")[\s\S]+?(?=")
        // (?<=version\s*=\s*')[\s\S]+?(?=')
        // 匹配 version="1.0.56" version='1.0.56'   的 1.0.56

        String[] patternStr = new String[]{"(?<=" + keyStr + "\\s{0,100}=\\s{0,100}\")[\\s\\S]+?(?=\")", "(?<=" + keyStr + "\\s{0,100}=\\s{0,100}')[\\s\\S]+?(?=')"};
        Pattern pattern = null;
        Matcher matcher = null;

        for (int i = 0; i < patternStr.length; i++) {
            pattern = Pattern.compile(patternStr[i]);
            matcher = pattern.matcher(matchHeadStr);
            if (matcher.find())//匹配成功
            {
                return matcher.group();
            }
        }
        return "";

    }

    // matchHeadStr ： 要匹配的字符串,尖括号内容  根节点内容（headStr）
    private String getTagStr(String matchHeadStr)// 获取根节点标签
    {
        // (?<=<)[^/><。?？."'‘’“”\\\s]+(?=(\s|>))
        // 匹配 根节点标签

        String patternStr = "(?<=<)[^/><。?？.\"'‘’“”\\\\\\s]+(?=(\\s|>))";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(matchHeadStr);
        if (matcher.find())//匹配成功
        {
            return matcher.group();
        }
        print("没有匹配到标签，出现异常");
        return "";
    }

    // matchXmlStr ： 要匹配的字符串,尖括号内容
    private String getHeadStr(String matchXmlStr)// 获取根节点 <tree age="100" name='白杨树'> 尖括号内容
    {
        // ^<[\w\W]+?>
        // 字符串开头 根节点头部

        String patternStr = "^<[\\w\\W]+?>";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(matchXmlStr);
        if (matcher.find())//匹配成功
        {
            return matcher.group();
        }
        return "";
    }

    // matchXmlStr ： 要匹配的字符串,尖括号内容
    private String getBodyStr(String matchXmlStr)// 获取根节点下的所有节点，或值
    {
        // (?<=>)[\w\W]+(?=</)
        // 匹配根节点下的所有子节点，或者值

        String patternStr = "(?<=>)[\\w\\W]+(?=</)";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(matchXmlStr);
        if (matcher.find())//匹配成功
        {
            return matcher.group();
        }
        return "";
    }

    // matchHeadStr ： 要匹配的字符串,尖括号内容  根节点内容（headStr）
    private List<String> getAttibutesKey(String matchHeadStr)//匹配所有根节点属性键
    {
        // (?<=\s)[^/><。?？."'‘’“”\\\s]+?(?=\s|=)
        // 匹配所有根节点内的属性键名

        List<String> strs = new ArrayList<>();
        String patternStr = "(?<=\\s)[^/><。?？.\"'‘’“”\\\\\\s]+?(?=\\s|=)";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(matchHeadStr);
        while (matcher.find()) {
            strs.add(matcher.group());
        }
        return strs;
    }

    // matchHeadStr ： 要匹配的字符串,尖括号内容  根节点内容（headStr）
    // attibutes    ： 要赋值的集合
    private void setAtttibutes(String matchHeadStr, HashMap<String, String> attibutes) {
        List<String> keys = getAttibutesKey(matchHeadStr);
        for (int i = 0; i < keys.size(); i++) {
            String value = getAttrValue(matchHeadStr, keys.get(i));
            if (!value.equals("") && !keys.get(i).equals("")) {
                attibutes.putIfAbsent(keys.get(i), value);
            }
        }
    }

    // matchBodyStr ： 所有的子类
    private List<String> getChildStrs(String matchBodyStr)//获取bodyStr子节点
    {
        List<String> Strs = new ArrayList<>();
        String tag = "";
        // ^<[\w]+(?=(\s|>))
        // 匹配标签
        String patternStr = "^<[\\w]+(?=(\\s|>))";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(matchBodyStr);
        while (matcher.find()) {
            tag = matcher.group().substring(1);
            // </book>
            // 匹配结束标签
            String s = "</" + tag + ">";
            Pattern p = Pattern.compile(s);
            Matcher m = p.matcher(matchBodyStr);
            while (m.find()) {
                if (getLayer(matchBodyStr.substring(0, m.end()), tag) == 0) {
                    Strs.add(matchBodyStr.substring(0, m.end()));
                    matchBodyStr = matchBodyStr.substring(m.end());
                    break;
                }
            }
            matcher = pattern.matcher(matchBodyStr);
        }
        return Strs;
    }

    // xml : 要匹配的字符串
    // tag : 要匹配的标签
    // return ： 返回tag在xml所在层级 0: 表示开始标签和结束标签在同一层级
    private int getLayer(String xml, String tag) {
        // <book(?=\s|>)
        // 匹配开始标签
        // </book>
        // 匹配结束标签

        int headTag = 0;
        int endTag = 0;
        String patternStr = "<" + tag + "(?=\\s|>)";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(xml);
        while (matcher.find()) {
            headTag++;
        }
        patternStr = "</" + tag + ">";
        pattern = Pattern.compile(patternStr);
        matcher = pattern.matcher(xml);
        while (matcher.find()) {
            endTag++;
        }
        return headTag - endTag;
    }

    private void parsingBody(XMLElement xmlElement)//解析节点获取所有子类
    {
        List<String> Strs = getChildStrs(xmlElement.bodyStr);
        xmlElement.childs = new ArrayList<>();
        for (int i = 0; i < Strs.size(); i++) {
            XMLElement child = new XMLElement();
            child.parent = xmlElement;
            child.bodyStr = getBodyStr(Strs.get(i));
            child.headStr = getHeadStr(Strs.get(i));
            child.tagStr = getTagStr(child.headStr);
            child.attibutes = new HashMap<>();
            setAtttibutes(child.headStr, child.attibutes);
            parsingBody(child);
            xmlElement.childs.add(child);
        }
    }

    //endregion
    //region 公开方法
    public String toString()//将 xmlObj 解析成xml  字符串
    {

        String xml = "";
        //region XML 编码 版本
        if (!version.equals("")) {
            xml += "<?xml version=\"" + version + "\"?>";
        }
        if (!encoding.equals("")) {
            if (xml.length() <= 1) {
                xml += "<?xml encoding=\"" + encoding + "\"?>";
            } else {
                xml = xml.replace("?>", " encoding=\""+encoding+"\"?>");
            }
        }
        //endregion
        //region xml 内容
        xml+=XMLElementToString(xmlObj);
        //endregion
        return xml;
    }
    //endregion


    public class XMLElement {
        //region 属性
        public XMLElement parent;
        public List<XMLElement> childs;
        public HashMap<String, String> attibutes;
        public String bodyStr;                     // 值  所有的子类，或值
        public String tagStr;                     // 标签
        public String headStr;                   // 尖括号内容   <book category="CHILDREN">
        //endregion
        //region 方法
        // tag    ： 子节点标签
        // return : 获取所有子类标签为tag的对象
        public List<XMLElement> getXMLElementsByTag(String tag)//查找节点树，通过标签
        {
            List<XMLElement> child = new ArrayList<>();
            if (childs != null) {
                for (int i = 0; i < childs.size(); i++) {
                    if (childs.get(i).tagStr.equals(tag)) {
                        child.add(childs.get(i));
                    }
                }
            }
            return child;
        }
        // tag    ： 子节点标签
        // return : 返回第一次匹配到的标签为tag的对象
        public XMLElement getXMLElementByTag(String tag)//查找节点树，通过标签
        {
            if (childs != null) {
                for (int i = 0; i < childs.size(); i++) {
                    if (childs.get(i).tagStr.equals(tag)) {
                        return childs.get(i);
                    }
                }
            }
            return null;
        }
        //endregion
    }
}